package com.dtranx.tools.common.utils;


import org.apache.commons.collections4.CollectionUtils;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description 列表数据映射工具类，适用于根据列表对象中的某个字段值，获取其他字段的值。
 * @Author zl
 * @Date 2024/8/30 15:01
 */
public class FieldMappingUtil {
    public static <T, K, V> void keyValueMapping(Collection<T> list,
                                                 Function<T, K> getKey,
                                                 Function<List<K>, List<V>> key2Values,
                                                 Function<V, K> valueGroup,
                                                 BiConsumer<T, List<V>> setGroupedValues) {
        keyValueMapping(list, getKey, key2Values, valueGroup, setGroupedValues, false);
    }

    public static <T, K, V> void keyValueMapping(Collection<T> list,
                                                 Function<T, K> getKey,
                                                 Function<List<K>, List<V>> key2Values,
                                                 Function<V, K> valueGroup,
                                                 BiConsumer<T, List<V>> setGroupedValues,
                                                 Consumer<Collection<T>> unmappedData) {
        boolean returnUnmappedData = Objects.nonNull(unmappedData);
        Collection<T> unmapped = keyValueMapping(list, getKey, key2Values,
                valueGroup, setGroupedValues, returnUnmappedData);
        if (returnUnmappedData && CollectionUtils.isNotEmpty(unmapped)) {
            unmappedData.accept(unmapped);
        }
    }

    public static <T, K, V, R> R keyValueMappingReturn(Collection<T> list,
                                                       Function<T, K> getKey,
                                                       Function<List<K>, List<V>> key2Values,
                                                       Function<V, K> valueGroup,
                                                       BiConsumer<T, List<V>> setGroupedValues,
                                                       Function<Collection<T>, R> unmappedData) {
        boolean returnUnmappedData = Objects.nonNull(unmappedData);
        Collection<T> unmapped = keyValueMapping(list, getKey, key2Values,
                valueGroup, setGroupedValues, returnUnmappedData);
        if (returnUnmappedData && CollectionUtils.isNotEmpty(unmapped)) {
            return unmappedData.apply(unmapped);
        }
        return null;
    }


    /**
     * 列表数据映射
     * @param list 原始列表数据
     * @param getKey 获取原始列表数据的key（与目标列表的key相互对应）
     * @param key2Values 根据原始数据的key获取目标列表数据
     * @param valueGroup 将目标列表数据根据key分组
     * @param setGroupedValues 获取对应key的目标列表数据并设置到原始列表数据中
     * @param returnUnmappedData 是否返回未映射的原始列表数据
     * @return 返回映射后的列表数据
     */
    private static <T, K, V> Collection<T> keyValueMapping(Collection<T> list,
                                                           Function<T, K> getKey,
                                                           Function<List<K>, List<V>> key2Values,
                                                           Function<V, K> valueGroup,
                                                           BiConsumer<T, List<V>> setGroupedValues,
                                                           boolean returnUnmappedData) {
        if (CollectionUtils.isNotEmpty(list)) {
            List<K> nonNullKeys = list.stream().map(getKey).filter(Objects::nonNull).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(nonNullKeys)) {
                List<V> allValues = key2Values.apply(nonNullKeys);
                if (CollectionUtils.isNotEmpty(allValues)) {
                    Map<K, List<V>> map = allValues.stream().collect(Collectors.groupingBy(valueGroup));
                    return list.stream().filter(t -> {
                        K key = getKey.apply(t);
                        List<V> values = map.get(key);
                        if (CollectionUtils.isNotEmpty(values)) {
                            setGroupedValues.accept(t, values);
                            return false;
                        } else {
                            return returnUnmappedData;
                        }
                    }).collect(Collectors.toList());
                }
            }
        }
        return returnUnmappedData ? list : null;
    }
}
